package com.ethink.framework.feign.config;

import com.ethink.framework.common.web.context.RequestContext;
import com.ethink.framework.common.web.context.SPRequestContextHolder;
import com.ethink.framework.feign.properties.SpringDocProperties;
import com.google.common.collect.Lists;
import io.swagger.v3.oas.models.*;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.QueryParameter;
import io.swagger.v3.oas.models.servers.Server;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


/**
 * @author yunduo
 */
@Configuration
@EnableConfigurationProperties({SpringDocProperties.class})
public class SpringDocConfig {

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private SpringDocProperties properties;

    @Bean
    public OpenAPI openAPI() {
        OpenAPI openAPI = new OpenAPI()
                .info(new Info().title(applicationContext.getApplicationName())
                        .description(applicationContext.getApplicationName())
                        .version("v1.0.0"));
        if (StringUtils.isNotBlank(properties.getServerUrl())) {
            Server server = new Server();
            server.setUrl(properties.getServerUrl());
            openAPI.servers(Lists.newArrayList(server));
        }
        return openAPI;
    }

    @Bean
    public OpenApiCustomiser openApiCustomiser() {
        return new OpenApiCustomiser() {
            @Override
            public void customise(OpenAPI openApi) {
                deal(openApi);
                RequestContext requestContext = SPRequestContextHolder.getRequestContext();
                if (requestContext != null && requestContext.getHttpHeads() != null) {
                    String url = requestContext.getHttpHeads().get("host");
                    if (StringUtils.isNotBlank(url)) {
                        Server server = new Server();
                        server.setUrl("http://" + url);
                        openApi.servers(Lists.newArrayList(server));
                        return;
                    }
                }
            }
        };
    }

    private void deal(OpenAPI openApi) {
        Paths paths = openApi.getPaths();
        for (Map.Entry<String, PathItem> entry : paths.entrySet()) {
            PathItem pathItem = entry.getValue();
            Operation operation = pathItem.getGet();
            if (operation != null && CollectionUtils.isNotEmpty(operation.getParameters())) {
                Iterator<Parameter> iterator = operation.getParameters().iterator();
                List<Parameter> newParameters = new ArrayList<>();
                while (iterator.hasNext()) {
                    Parameter parameter = iterator.next();
                    if ("request".equals(parameter.getName())) {
                        Schema schema = findSchema(openApi, parameter.getSchema().get$ref());
                        if (schema != null) {
                            iterator.remove();
                            Map<String, Schema> properties = schema.getProperties();
                            newParameters.addAll(buildParameters(properties, schema.getRequired()));
                        }
                    }
                }
                operation.getParameters().addAll(newParameters);
            }
        }
    }

    private List<Parameter> buildParameters(Map<String, Schema> properties, List<String> requiredList) {
        List<Parameter> parameters = new ArrayList<>();
        if (MapUtils.isNotEmpty(properties)) {
            for (Schema schema : properties.values()) {
                Parameter parameter = new QueryParameter();
                parameter.setName(schema.getName());
                parameter.setDescription(schema.getDescription());
                if (CollectionUtils.isNotEmpty(requiredList) && requiredList.contains(schema.getName())) {
                    parameter.setRequired(true);
                }
                parameter.setDeprecated(schema.getDeprecated());
                parameter.setAllowEmptyValue(schema.getNullable());
                parameter.setSchema(schema);
                parameter.setExample(schema.getExample());
                parameters.add(parameter);
            }
        }
        return parameters;
    }

    private Schema findSchema(OpenAPI openApi, String ref) {
        Components components = openApi.getComponents();
        Map<String, Schema> schemas = components.getSchemas();
        Schema schema = schemas.get(ref.replace("#/components/schemas/", ""));
        return schema;
    }


}